package com.xhui.vimarket.zkplus.databind;

/*
 * AnnotateDataBinderInit.java {{IS_NOTE Purpose: Description: History: Thu Nov
 * 30 17:41:15 2006, Created by Henri Chen }}IS_NOTE Copyright (C) 2006 Potix
 * Corporation. All Rights Reserved. {{IS_RIGHT }}IS_RIGHT
 */

import java.util.Map;

import org.zkoss.lang.Library;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.Page;
import org.zkoss.zk.ui.Path;
import org.zkoss.zk.ui.UiException;
import org.zkoss.zk.ui.util.Initiator;
import org.zkoss.zk.ui.util.InitiatorExt;
import org.zkoss.zkplus.databind.AnnotateDataBinder;

/**
 * <p>
 * This initiator class do following things:
 * </p>
 * <ol>
 * <li>New an {@link AnnotateDataBinder} instance.</li>
 * <li>Set the AnnotateDataBinder instance as a custom attribute with the name
 * as specified in arg2 (default to "binder") and store it in the component as
 * specified in arg0 "component-path".(if arg0 is not specified, use Page
 * instead.)</li>
 * <li>call {@link AnnotateDataBinder#loadAll()} in to
 * {@link #doAfterCompose(Page)} and initiate all UI components from the
 * associated data bean.</li>
 * </ol>
 * <p>
 * Put the init PI as follows:
 * </p>
 * 
 * <pre>
 * &lt;?init class="org.zkoss.zkplus.databind.AnnotateDataBinderInit" 
 * 	[root="component|component-path"] 
 *  [loadDefault="true|false"]
 *  [name="binder's name"] ?>
 * </pre>
 * <p>
 * Where the root attribute is the component itself (via EL expression) or the
 * component path that specifies the component the AnnotateDataBinder covers.
 * You can use absolute path that starts with "//" (search from Desktop) or "/"
 * (search from Page); or you can use relative path(supported since ZK 3.0.8)
 * that starts with "./" or "../" (relative to the Id Space of the root
 * component). If the root attribute is not specified or set to string "page",
 * the AnnotateDataBinder will default to cover the whole page.
 * </p>
 * <p>
 * Where the loadDefault attribute is used to decide whether to load default
 * binding configuration defined in lang-addon.xml. If the loadDefault attribute
 * is not specified it is default to true.
 * </p>
 * <p>
 * (since 3.6.2) Where the name attribute is used to specify the created
 * DataBinder's name (default to "binder") which you can access it via EL or
 * component.getAttribute(name) later.
 * <p>
 * For application design to run ZK prior to 3.6.2, it can use arg0 instead of
 * root, and arg1 instead of loadDefault.
 * <p>
 * Note that since 5.0, the created DataBinder is stored in component scope of
 * the root component rather than in the IdSpace scope of the root component. If
 * that is important to you, you can specify compatible library property in
 * WEB-INF/zk.xml to true to make it work as it was prior version 5; i.e. store
 * the created DataBinder in IdSpace scope of the root component.
 * 
 * <pre>
 * 	&lt;library-property>
 * 	&lt;name>org.zkoss.zkplus.databind.AnnotateDataBinderInit.compatible&lt;/name>
 * 	&lt;value>true&lt;/value>
 * &lt;/library-property>
 * </pre>
 * 
 * @author Henri Chen
 * @see AnnotateDataBinder
 */
public class AnnotateDataBinderInitPlus implements Initiator, InitiatorExt {
    /**
     * .
     */
    private static final String  COMPATIBLE = "org.zkoss.zkplus.databind.AnnotateDataBinderInit.compatible";
    /**
     * .
     */
    private Component            comp;
    /**
     * .
     */
    private String               compPath;
    /**
     * .
     */
    private String               defaultConfig;
    /**
     * .
     */
    private String               name;
    
    /** The AnnotateDataBinder created in doAfterCompose() */
    /**
     * .
     */
    protected AnnotateDataBinder binder;
    
    // -- Initator --//
    /**
     * {@inheritDoc}
     * @see org.zkoss.zk.ui.util.Initiator#doAfterCompose(org.zkoss.zk.ui.Page)
     * @author wuyx
     * 2011-1-25 wuyx
     */
    public void doAfterCompose(final Page page) {
        // will not be called since we implements InitiatorExt
    }
    
    /**
     * {@inheritDoc}
     * @see org.zkoss.zk.ui.util.Initiator#doCatch(java.lang.Throwable)
     * @author wuyx
     * 2011-1-25 wuyx
     */
    public boolean doCatch(final java.lang.Throwable ex) {
        return false; // do nothing
    }
    
    /**
     * {@inheritDoc}
     * @see org.zkoss.zk.ui.util.Initiator#doFinally()
     * @author wuyx
     * 2011-1-25 wuyx
     */
    public void doFinally() {
        // do nothing
    }
    
    /**
     * {@inheritDoc}
     * @see org.zkoss.zk.ui.util.Initiator#doInit(org.zkoss.zk.ui.Page, java.util.Map)
     * @author wuyx
     * 2011-1-25 wuyx
     */
    public void doInit(final Page page, final Map args) {
        boolean hasArg0 = args.containsKey("root");
        
        Object arg0 = null;
        if (hasArg0) {
            arg0 = args.get("root");
        } else {
            hasArg0 = args.containsKey("arg0");
            if (hasArg0) {
                arg0 = args.get("arg0"); // backward compatible
            }
        }
        if (hasArg0) {
            if (arg0 instanceof String) {
                this.compPath = (String) arg0;
            } else if (arg0 instanceof Component) {
                this.comp = (Component) arg0;
            } else if (!(arg0 instanceof Page)) {
                throw new UiException("arg0/root has to be String, Component, or Page: " + arg0);
            }
        }
        
        this.defaultConfig = (String) args.get("loadDefault");
        if (this.defaultConfig == null) {
            this.defaultConfig = (String) args.get("arg1"); // backward
            // compatible
        }
        
        this.name = (String) args.get("name");
        if (this.name == null) {
            this.name = "binder";
        }
    }
    
    // -- InitiatorExt --//
    /**
     * .
     * 
     * @param comp
     * @author wuyx
     * 2011-1-25 wuyx 
     */
    private void saveBinder(final Component comp) {
        final String val = Library.getProperty(AnnotateDataBinderInitPlus.COMPATIBLE);
        if ("true".equals(val)) {
            comp.setAttribute(this.name, this.binder, Component.SPACE_SCOPE);
        } else {
            comp.setAttribute(this.name, this.binder);
        }
    }
    
    /**
     * {@inheritDoc}
     * @see org.zkoss.zk.ui.util.InitiatorExt#doAfterCompose(org.zkoss.zk.ui.Page, org.zkoss.zk.ui.Component[])
     * @author wuyx
     * 2011-1-25 wuyx
     */
    public void doAfterCompose(final Page page, final Component[] comps) throws Exception {
        final boolean b = this.defaultConfig != null ? Boolean.valueOf(this.defaultConfig)
            .booleanValue()
            : true;
        if (this.comp instanceof Component) { // a specified component instance
            this.binder = new AnnotateDataBinder(this.comp, b);
            this.saveBinder(this.comp); // _comp.setAttribute(_name, _binder);
        } else if (this.compPath == null || "page".equals(this.compPath)) { // page
            this.binder = new AnnotateDataBinder(page, b);
            if (page.getAttribute(this.name) != null) { // already a binder on
                // the
                // page
                // throw new
                // UiException("Page is already covered by another Data Binder. Cannot be covered by this Data Binder again. Page:"+page.getId());
                return; // change by wuyx, do not to set binder
            } else {
                page.setAttribute(this.name, this.binder);
            }
        } else if (this.compPath.startsWith("/")) { // absolute path
            final Component comp = Path.getComponent(this.compPath);
            if (comp == null) {
                // throw new
                // UiException("Cannot find the specified component. Absolute Path:"+_compPath);
                return; // change by wuyx, do not to set binder
            }
            this.binder = new AnnotateDataBinder(comp, b);
            this.saveBinder(comp); // comp.setAttribute(_name, _binder);
        } else if (this.compPath.startsWith("./") || this.compPath.startsWith("../")) { // relative
            // path
            for (int j = 0; j < comps.length; ++j) {
                final Component vroot = comps[j];
                final Component comp = Path.getComponent(vroot.getSpaceOwner(), this.compPath);
                if (comp != null) { // found
                    this.binder = new AnnotateDataBinder(comp, b);
                    this.saveBinder(comp); // comp.setAttribute(_name, _binder);
                    break;
                }
            }
            if (this.binder == null) {
                throw new UiException("Cannot find the specified component. Relative Path:"
                    + this.compPath);
            }
        } else {
            final Component comp = page.getFellow(this.compPath);
            this.binder = new AnnotateDataBinder(comp, b);
            this.saveBinder(comp); // comp.setAttribute(_name, _binder);
        }
        this.binder.loadAll(); // load data bean properties into UI components
    }
}
